package org.icop.frame.select;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.Vector;

import org.icop.frame.eval.Evaluator;
import org.icop.model.MultiSet;
import org.icop.model.ValuedCorrespondence;
import org.jbpt.graph.abs.IEdge;
import org.jbpt.graph.abs.IGraph;
import org.jbpt.hypergraph.abs.IVertex;


public class GreedyMatchMappingCombinedSelector<G extends IGraph<E,V>, E extends IEdge<V>, V extends IVertex> extends AbstractSelector<G, E, V> implements Selector<G, E, V> {

	public double mappingSimFactor = 0.8;
	
	public double matchSimFactor = 0.2;
	
	public GreedyMatchMappingCombinedSelector(double mappingSimFactor, double matchSimFactor) {
		this.mappingSimFactor = mappingSimFactor;
		this.matchSimFactor = matchSimFactor;
	}
	
	@Override
	public Set<ValuedCorrespondence<V>> select(G sg1, G sg2,
			Evaluator<G, E, V> evaluator,
			MultiSet<ValuedCorrespondence<V>> viableMappings) {

		assert(evaluator != null);
		
		//INIT
		Set<ValuedCorrespondence<V>> mapping  = new HashSet<ValuedCorrespondence<V>>();
		Set<ValuedCorrespondence<V>> openMappings = new HashSet<ValuedCorrespondence<V>>(viableMappings);
		double highestSimilarity = Double.MIN_VALUE;
		Random randomized = new Random();

		//FIRST STEP
		double maxSim = getMaxMatchSim(viableMappings);
		boolean doStep = true;
		while (doStep){
			doStep = false;
			Vector<ValuedCorrespondence<V>> bestCandidates = new Vector<ValuedCorrespondence<V>>();
			
			for (ValuedCorrespondence<V> couple: openMappings){
				if (couple.getConfidenceValue() == maxSim)
					bestCandidates.add(couple);
			}
			
			if (bestCandidates.size() > 0){
				//Choose a random candidate
				ValuedCorrespondence<V> couple = bestCandidates.get(randomized.nextInt(bestCandidates.size()));

				Set<ValuedCorrespondence<V>> newOpenMappings = new HashSet<ValuedCorrespondence<V>>();
				for (ValuedCorrespondence<V> p: openMappings){
					if (!overlaps(p.getV1s(),couple.getV1s()) && !overlaps(p.getV2s(),couple.getV2s())){
						newOpenMappings.add(p);
					}
				}
				openMappings = newOpenMappings;					
				mapping.add(couple);
				doStep = true;
			}			
		}
		
		//SECOND STEP
		doStep = true;
		while (doStep){
			doStep = false;
			Vector<ValuedCorrespondence<V>> bestCandidates = new Vector<ValuedCorrespondence<V>>();
			double newHighestSimilarity = highestSimilarity;

			for (ValuedCorrespondence<V> couple: openMappings){
				Set<ValuedCorrespondence<V>> newMapping = new HashSet<ValuedCorrespondence<V>>(mapping);
				newMapping.add(couple);
				
				double mappingSim = evaluator.evaluate(sg1, sg2, newMapping); 
				double matchSim = couple.getConfidenceValue();
				
				double newSimilarity = mappingSimFactor * mappingSim + matchSimFactor * matchSim; 

				if (newSimilarity > newHighestSimilarity){
					bestCandidates = new Vector<ValuedCorrespondence<V>>();
					bestCandidates.add(couple);
					newHighestSimilarity = newSimilarity;
				}else if (newSimilarity == newHighestSimilarity){
					bestCandidates.add(couple);
				}
			}

			if (bestCandidates.size() > 0){
				//Choose a candidate based on match sim
				ValuedCorrespondence<V> couple = getMaxSimMatch(bestCandidates);
						
				Set<ValuedCorrespondence<V>> newOpenMappings = new HashSet<ValuedCorrespondence<V>>();
				for (ValuedCorrespondence<V> p: openMappings){
					if (!overlaps(p.getV1s(),couple.getV1s()) && !overlaps(p.getV2s(),couple.getV2s())){
						newOpenMappings.add(p);
					}
				}
				openMappings = newOpenMappings;					

				mapping.add(couple);
				highestSimilarity = newHighestSimilarity;
				doStep = true;
			}			
		}

		//Return the smallest edit distance
		return mapping;

	}

}
